package org.jlamp.boot.vm;

import com.sun.tools.attach.*;
import org.jlamp.boot.runtime.JlampInfo;
import org.jlamp.core.prop.SystemProp;
import org.jlamp.core.runtime.JlampData;

import java.io.File;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 虚拟机工具类
 *
 * @author zhangcs
 */
public class VmUtils {

    private static final PrintStream LOGGER = System.err;

    static {
        // java lib
        boolean loaded = loadTools(SystemProp.JAVA_HOME + "/../lib/tools.jar");
        if (!loaded) {
            // system env
            String env = SystemProp.JAVA_HOME_ENV;
            loaded = loadTools(env + "/lib/tools.jar");
            if (!loaded) {
                LOGGER.println("(err) tools.jar not found. ");
            }
        }
    }

    /**
     * 加载 tools.jar 文件
     *
     * @param path 文件路径
     * @return 返回加载状态，成功时返回true
     */
    private static boolean loadTools(String path) {
        File file = new File(path);
        if (file.exists()) {
            try {
                URL url = file.toURI().toURL();

                Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
                method.setAccessible(true);

                URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
                method.invoke(classLoader, url);
                return true;
            } catch (Exception e) {
                // ignore
            }
        }
        return false;
    }

    /**
     * java进程列表
     */
    private static List<VirtualMachineDescriptor> descriptorList = new ArrayList<>(16);

    /**
     * 虚拟机进程对象
     */
    private static VirtualMachine virtualMachine;

    /**
     * 获取虚拟机进程列表
     *
     * @return 返回进程信息列表
     */
    public static List<String> list() {
        // 每次查看进程列表时重新获取
        descriptorList.clear();

        List<VirtualMachineDescriptor> descriptors = VirtualMachine.list();
        VmUtils.descriptorList = descriptors;

        return descriptors.stream()
                .map(descriptor -> {
                    String id = descriptor.id();
                    String name = descriptor.displayName();
                    if (name != null) {
                        name = name.split("\\s")[0];
                    }
                    return "  [ " + id + " ]" + "\t" + name;
                }).collect(Collectors.toList());
    }

    /**
     * 获取当前虚拟机进程列表id集合
     *
     * @return 返回id集合
     */
    public static List<String> ids() {
        if (descriptorList.isEmpty()) {
            list();
        }

        return descriptorList.stream().map(VirtualMachineDescriptor::id)
                .collect(Collectors.toList());
    }

    /**
     * attach 进程
     *
     * @param id 进程号
     * @return 返回操作状态，成功时返回true
     */
    public static boolean attach(String id) {
        if (JlampInfo.PROCESS_ID != null) {
            System.err.println("Please exit process " + JlampInfo.PROCESS_ID + " first!");
            return false;
        }
        try {
            virtualMachine = VirtualMachine.attach(id);
            virtualMachine.loadAgent(JlampInfo.AGENT_FILE, JlampInfo.SERVER_FILE);

            JlampInfo.PROCESS_ID = id;

            int num = 0;
            while (true) {
                JlampData data = JlampData.get(id);
                String port = data.port();
                if (port != null) {
                    LOGGER.println("Attach " + id + " (Java Virtual Machine) Successful");
                    LOGGER.println("Server Listening Port " + port + ".");
                    break;
                }

                if (num++ > (60 * 2)) {
                    throw new RuntimeException("start server failed");
                }

                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                    // ignore
                }
            }

            return true;
        } catch (Exception e) {
            LOGGER.println(e.getMessage());
            e.printStackTrace();
        }
        return false;
    }

    /**
     * detach 进程
     *
     * @return 返回操作状态，成功时返回true
     */
    public static boolean detach() {
        if (virtualMachine == null) {
            return true;
        }

        try {
            virtualMachine.detach();
            virtualMachine = null;

            // 停止服务
            JlampData.get(JlampInfo.PROCESS_ID).stop();
            LOGGER.println("Detach, Stop Server for " + JlampInfo.PROCESS_ID);

            JlampInfo.PROCESS_ID = null;

            return true;
        } catch (Exception e) {
            LOGGER.println(e.getMessage());
        } finally {
            descriptorList.clear();
        }
        return false;
    }

    /**
     * 已附加进程
     *
     * @return 已附加返回true，否则返回false
     */
    public static boolean isAttaching() {
        return virtualMachine != null;
    }

}
